home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / WINGs / connection.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-30  |  25.2 KB  |  940 lines

  1. /*
  2.  *  WINGs WMConnection function library
  3.  * 
  4.  *  Copyright (c) 1999 Dan Pascu
  5.  * 
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; if not, write to the Free Software
  18.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21.  
  22. /*
  23.  * TODO:
  24.  * - decide if we want to support connections with external sockets, else
  25.  *   clean up the structure of the unneeded members.
  26.  *
  27.  */
  28.  
  29.  
  30. #include "../src/config.h"
  31.  
  32. #include <unistd.h>
  33. #include <fcntl.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <stdarg.h>
  38. #include <errno.h>
  39. #include <sys/socket.h>
  40. #include <netinet/in.h>
  41. #include <arpa/inet.h>
  42. #include <netdb.h>
  43. #include <signal.h>
  44. #ifdef __FreeBSD__
  45. #include <sys/signal.h>
  46. #endif
  47.  
  48. #include "WINGs.h"
  49.  
  50.  
  51. /* Some older systems does not define this (linux libc5, maybe others too) */
  52. #ifndef SHUT_RDWR
  53. # define SHUT_RDWR              2
  54. #endif
  55.  
  56. /* for SunOS */
  57. #ifndef SA_RESTART
  58. # define SA_RESTART             0
  59. #endif
  60.  
  61. /* Stuff for setting the sockets into non-blocking mode. */
  62. /*#ifdef    __POSIX_SOURCE
  63. # define NONBLOCK_OPT           O_NONBLOCK
  64. #else
  65. # define NONBLOCK_OPT           FNDELAY
  66. #endif*/
  67.  
  68. #define NONBLOCK_OPT            O_NONBLOCK
  69.  
  70.  
  71. #define NETBUF_SIZE             4096
  72.  
  73.  
  74. #define DEF_TIMEOUT             600   /* 600 seconds == 10 minutes */
  75.  
  76.  
  77. int WCErrorCode = 0;
  78.  
  79.  
  80.  
  81. static Bool SigInitialized = False;
  82.  
  83.  
  84.  
  85. typedef struct TimeoutData {
  86.     unsigned timeout;
  87.     WMHandlerID *handler;
  88. } TimeoutData;
  89.  
  90.  
  91.  
  92. typedef struct W_Connection {
  93.     int sock;                     /* the socket we speak through */
  94.  
  95.     struct {
  96.         WMHandlerID *read;        /* the input read handler */
  97.         WMHandlerID *write;       /* the input write handler */
  98.         WMHandlerID *exception;   /* the input exception handler */
  99.     } handler;
  100.  
  101.     ConnectionDelegate *delegate; /* client delegates */
  102.     void *clientData;             /* client data */
  103.     unsigned int uflags;          /* flags for the client */
  104.  
  105.     WMBag *outputQueue;
  106.     unsigned bufPos;
  107.  
  108.     TimeoutData sendTimeout;
  109.  
  110.     WMConnectionState state;
  111.  
  112.     char *address;
  113.     char *service;
  114.     char *protocol;
  115.  
  116.     Bool closeOnRelease;
  117.     Bool wasNonBlocking;
  118.     Bool isNonBlocking;
  119.  
  120. } W_Connection;
  121.  
  122.  
  123.  
  124.  
  125. static void
  126. clearOutputQueue(WMConnection *cPtr) /*FOLD00*/
  127. {
  128.     int i;
  129.  
  130.     cPtr->bufPos = 0;
  131.  
  132.     for (i=0; i<WMGetBagItemCount(cPtr->outputQueue); i++)
  133.         WMReleaseData(WMGetFromBag(cPtr->outputQueue, i));
  134.  
  135.     WMEmptyBag(cPtr->outputQueue);
  136. }
  137.  
  138.  
  139. static void
  140. sendTimeout(void *cdata) /*FOLD00*/
  141. {
  142.     WMConnection *cPtr = (WMConnection*) cdata;
  143.     TimeoutData *tPtr = &cPtr->sendTimeout;
  144.  
  145.     tPtr->handler = NULL;
  146.     if (cPtr->handler.write) {
  147.         WMDeleteInputHandler(cPtr->handler.write);
  148.         cPtr->handler.write = NULL;
  149.     }
  150.     if (WMGetBagItemCount(cPtr->outputQueue)>0) {
  151.         clearOutputQueue(cPtr);
  152.         if (cPtr->delegate && cPtr->delegate->didTimeout)
  153.             (*cPtr->delegate->didTimeout)(cPtr->delegate, cPtr);
  154.     }
  155. }
  156.  
  157.  
  158. static void
  159. inputHandler(int fd, int mask, void *clientData) /*FOLD00*/
  160. {
  161.     WMConnection *cPtr = (WMConnection*)clientData;
  162.  
  163.     if (cPtr->state==WCClosed || cPtr->state==WCDied)
  164.         return;
  165.  
  166.     if ((mask & WIWriteMask)) {
  167.         if (cPtr->state == WCInProgress) {
  168.             int    result;
  169.             int    len = sizeof(result);
  170.  
  171.             if (getsockopt(cPtr->sock, SOL_SOCKET, SO_ERROR,
  172.                            (void*)&result, &len) == 0 && result != 0) {
  173.                 cPtr->state = WCFailed;
  174.                 WCErrorCode = result;
  175.                 /* should call wsyserrorwithcode(result, ...) here? */
  176.             } else {
  177.                 cPtr->state = WCConnected;
  178.             }
  179.  
  180.             if (cPtr->handler.write) {
  181.                 WMDeleteInputHandler(cPtr->handler.write);
  182.                 cPtr->handler.write = NULL;
  183.             }
  184.  
  185.             if (cPtr->delegate && cPtr->delegate->didInitialize)
  186.                 (*cPtr->delegate->didInitialize)(cPtr->delegate, cPtr);
  187.  
  188.             if (cPtr->state == WCFailed)
  189.                 return;
  190.         } else if (cPtr->state == WCConnected) {
  191.             WMFlushConnection(cPtr);
  192.         }
  193.     }
  194.  
  195.     if (!cPtr->delegate)
  196.         return;
  197.  
  198.     /* if the connection died, may get destroyed in the delegate, so retain */
  199.     wretain(cPtr);
  200.  
  201.     if ((mask & WIReadMask) && cPtr->delegate->didReceiveInput)
  202.         (*cPtr->delegate->didReceiveInput)(cPtr->delegate, cPtr);
  203.  
  204.     if ((mask & WIExceptMask) && cPtr->delegate->didCatchException)
  205.         (*cPtr->delegate->didCatchException)(cPtr->delegate, cPtr);
  206.  
  207.     wrelease(cPtr);
  208. }
  209.  
  210.  
  211. static Bool
  212. setSocketNonBlocking(int sock, Bool flag) /*FOLD00*/
  213. {
  214.     int state;
  215.     Bool isNonBlock;
  216.  
  217.     state = fcntl(sock, F_GETFL, 0);
  218.  
  219.     if (state < 0) {
  220.         wsyserror("Failed to get socket flags with fcntl.");
  221.         return False;
  222.     }
  223.  
  224.     isNonBlock = (state & NONBLOCK_OPT) != 0;
  225.  
  226.     if (flag) {
  227.         if (isNonBlock)
  228.             return True;
  229.         state |= NONBLOCK_OPT;
  230.     } else {
  231.         if (!isNonBlock)
  232.             return True;
  233.         state &= ~NONBLOCK_OPT;
  234.     }
  235.  
  236.     if (fcntl(sock, F_SETFL, state) < 0) {
  237.         wsyserror("Failed to set socket flags with fcntl.");
  238.         return False;
  239.     }
  240.  
  241.     return True;
  242. }
  243.  
  244.  
  245. static void
  246. setConnectionAddress(WMConnection *cPtr, struct sockaddr_in *socketaddr) /*FOLD00*/
  247. {
  248.     wassertr(cPtr->address==NULL);
  249.  
  250.     cPtr->address = wstrdup(inet_ntoa(socketaddr->sin_addr));
  251.     cPtr->service = wmalloc(16);
  252.     sprintf(cPtr->service, "%hu", ntohs(socketaddr->sin_port));
  253.     cPtr->protocol = wstrdup("tcp");
  254. }
  255.  
  256.  
  257. static struct sockaddr_in*
  258. getSocketAddress(char* name, char* service, char* protocol) /*FOLD00*/
  259. {
  260.     static struct sockaddr_in socketaddr;
  261.     struct servent *sp;
  262.  
  263.     if (!protocol || protocol[0]=='\0')
  264.         protocol = "tcp";
  265.  
  266.     memset(&socketaddr, 0, sizeof(struct sockaddr_in));
  267.     socketaddr.sin_family = AF_INET;
  268.  
  269.     /*
  270.      * If we were given a hostname, we use any address for that host.
  271.      * Otherwise we expect the given name to be an address unless it is
  272.      * NULL (any address).
  273.      */
  274.     if (name && name[0]!='\0') {
  275.         WMHost *host = WMGetHostWithName(name);
  276.  
  277.         if (!host)
  278.             return NULL; /* name is not a hostname nor a number and dot adr */
  279.  
  280.         name = WMGetHostAddress(host);
  281. #ifndef    HAVE_INET_ATON
  282.         if ((socketaddr.sin_addr.s_addr = inet_addr(name)) == INADDR_NONE) {
  283. #else
  284.         if (inet_aton(name, &socketaddr.sin_addr) == 0) {
  285. #endif
  286.             WMReleaseHost(host);
  287.             return NULL;
  288.         }
  289.         WMReleaseHost(host);
  290.     } else {
  291.         socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  292.     }
  293.  
  294.     if (!service || service[0]=='\0') {
  295.         socketaddr.sin_port = 0;
  296.     } else if ((sp = getservbyname(service, protocol))==0) {
  297.         char *endptr;
  298.         unsigned portNumber;
  299.  
  300.         portNumber = strtoul(service, &endptr, 10);
  301.  
  302.         if (service[0]!='\0' && *endptr=='\0' && portNumber<65536) {
  303.             socketaddr.sin_port = htons(portNumber);
  304.         } else {
  305.             return NULL;
  306.         }
  307.     } else {
  308.         socketaddr.sin_port = sp->s_port;
  309.     }
  310.  
  311.     return &socketaddr;
  312. }
  313.  
  314.  
  315. static WMConnection*
  316. createConnectionWithSocket(int sock, Bool closeOnRelease) /*FOLD00*/
  317. {
  318.     WMConnection *cPtr;
  319.     struct sigaction sig_action;
  320.  
  321.     cPtr = wmalloc(sizeof(WMConnection));
  322.     wretain(cPtr);
  323.     memset(cPtr, 0, sizeof(WMConnection));
  324.  
  325.     cPtr->sock = sock;
  326.     cPtr->sendTimeout.timeout = DEF_TIMEOUT;
  327.     cPtr->sendTimeout.handler = NULL;
  328.     cPtr->closeOnRelease = closeOnRelease;
  329.     cPtr->outputQueue = WMCreateBag(16);
  330.     cPtr->state = WCNotConnected;
  331.  
  332.     /* ignore dead pipe */
  333.     if (!SigInitialized) {
  334.         sig_action.sa_handler = SIG_IGN;
  335.         sig_action.sa_flags = SA_RESTART;
  336.         sigaction(SIGPIPE, &sig_action, NULL);
  337.         SigInitialized = True;
  338.     }
  339.  
  340.     return cPtr;
  341. }
  342.  
  343.  
  344. #if 0
  345. WMConnection*
  346. WMCreateConnectionWithSocket(int sock, Bool closeOnRelease) /*FOLD00*/
  347. {
  348.     WMConnection *cPtr;
  349.     struct sockaddr_in clientname;
  350.     int size, n;
  351.  
  352.     cPtr = createConnectionWithSocket(sock, closeOnRelease);
  353.     cPtr->wasNonBlocking = WMIsConnectionNonBlocking(cPtr);
  354.     cPtr->isNonBlocking = cPtr->wasNonBlocking;
  355.  
  356.     /* some way to find out if it is connected, and binded. can't find
  357.        if it listens though!!!
  358.     */  
  359.  
  360.     size = sizeof(clientname);
  361.     n = getpeername(sock, (struct sockaddr*) &clientname, &size);
  362.     if (n==0) {
  363.         /* Since we have a peer, it means we are connected */
  364.         cPtr->state = WCConnected;
  365.     } else {
  366.         size = sizeof(clientname);
  367.         n = getsockname(sock, (struct sockaddr*) &clientname, &size);
  368.         if (n==0) {
  369.             /* We don't have a peer, but we are binded to an address.
  370.              * Assume we are listening on it (we don't know that for sure!)
  371.              */
  372.             cPtr->state = WCListening;
  373.         } else {
  374.             cPtr->state = WCNotConnected;
  375.         }
  376.     }
  377.  
  378.     return cPtr;
  379. }
  380. #endif
  381.  
  382.  
  383. /*
  384.  * host     is the name on which we want to listen for incoming connections,
  385.  *          and it must be a name of this host, or NULL if we want to listen
  386.  *          on any incoming address.
  387.  * service  is either a service name as present in /etc/services, or the port
  388.  *          number we want to listen on. If NULL, a random port between
  389.  *          1024 and 65535 will be assigned to us.
  390.  * protocol is one of "tcp" or "udp". If NULL, "tcp" will be used by default.
  391.  *          currently only "tcp" is supported.
  392.  */
  393. WMConnection*
  394. WMCreateConnectionAsServerAtAddress(char *host, char *service, char *protocol) /*FOLD00*/
  395. {
  396.     WMConnection *cPtr;
  397.     struct sockaddr_in *socketaddr;
  398.     int sock, size, on;
  399.  
  400.     if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
  401.         WCErrorCode = 0;
  402.         wwarning("Bad address-service-protocol combination");
  403.         return NULL;
  404.     }
  405.  
  406.     /* Create the actual socket */
  407.     sock = socket(PF_INET, SOCK_STREAM, 0);
  408.     if (sock<0) {
  409.         WCErrorCode = errno;
  410.         wsyserror("Unable to create socket");
  411.         return NULL;
  412.     }
  413.  
  414.     /*
  415.      * Set socket options. We try to make the port reusable and have it
  416.      * close as fast as possible without waiting in unnecessary wait states
  417.      * on close.
  418.      */
  419.     on = 1;
  420.     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
  421.  
  422.     if (bind(sock, (struct sockaddr *)socketaddr, sizeof(*socketaddr)) < 0) {
  423.         WCErrorCode = errno;
  424.         wsyserror("Unable to bind to address '%s:%hu'",
  425.                   inet_ntoa(socketaddr->sin_addr),
  426.                   ntohs(socketaddr->sin_port));
  427.         close(sock);
  428.         return NULL;
  429.     }
  430.  
  431.     if (listen(sock, 10) < 0) {
  432.         WCErrorCode = errno;
  433.         wsyserror("Unable to listen on port '%hu'",
  434.                   ntohs(socketaddr->sin_port));
  435.         close(sock);
  436.         return NULL;
  437.     }
  438.  
  439.     /* Find out what is the address/service/protocol we get */
  440.     /* In case some of address/service/protocol were NULL */
  441.     size = sizeof(*socketaddr);
  442.     if (getsockname(sock, (struct sockaddr*)socketaddr, &size) < 0) {
  443.         WCErrorCode = errno;
  444.         wsyserror("Unable to get socket address");
  445.         close(sock);
  446.         return NULL;
  447.     }
  448.  
  449.     cPtr = createConnectionWithSocket(sock, True);
  450.     cPtr->state = WCListening;
  451.     WMSetConnectionNonBlocking(cPtr, True);
  452.  
  453.     setConnectionAddress(cPtr, socketaddr);
  454.  
  455.     return cPtr;
  456. }
  457.  
  458.  
  459. WMConnection*
  460. WMCreateConnectionToAddress(char *host, char *service, char *protocol) /*FOLD00*/
  461. {
  462.     WMConnection *cPtr;
  463.     struct sockaddr_in *socketaddr;
  464.     int sock;
  465.  
  466.     if (service==NULL || service[0]=='\0') {
  467.         WCErrorCode = 0;
  468.         wwarning("Bad argument - service is not specified");
  469.         return NULL;
  470.     }
  471.  
  472.     if (host==NULL || host[0]=='\0')
  473.         host = "localhost";
  474.  
  475.     if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
  476.         WCErrorCode = 0;
  477.         wwarning("Bad address-service-protocol combination");
  478.         return NULL;
  479.     }
  480.  
  481.     /* Create the actual socket */
  482.     sock = socket(PF_INET, SOCK_STREAM, 0);
  483.     if (sock<0) {
  484.         WCErrorCode = errno;
  485.         wsyserror("Unable to create socket");
  486.         return NULL;
  487.     }
  488.     /* make socket blocking while we connect. */
  489.     setSocketNonBlocking(sock, False);
  490.     if (connect(sock, (struct sockaddr*)socketaddr, sizeof(*socketaddr)) < 0) {
  491.         WCErrorCode = errno;
  492.         wsyserror("Unable to make connection to address '%s:%hu'",
  493.                   inet_ntoa(socketaddr->sin_addr),
  494.                   ntohs(socketaddr->sin_port));
  495.         close(sock);
  496.         return NULL;
  497.     }
  498.  
  499.     cPtr = createConnectionWithSocket(sock, True);
  500.     cPtr->state = WCConnected;
  501.     WMSetConnectionNonBlocking(cPtr, True);
  502.     setConnectionAddress(cPtr, socketaddr);
  503.  
  504.     return cPtr;
  505. }
  506.  
  507.  
  508. WMConnection*
  509. WMCreateConnectionToAddressAndNotify(char *host, char *service, char *protocol) /*FOLD00*/
  510. {
  511.     WMConnection *cPtr;
  512.     /*TimeoutData *tPtr;*/
  513.     struct sockaddr_in *socketaddr;
  514.     int sock;
  515.     Bool isNonBlocking;
  516.  
  517.     if (service==NULL || service[0]=='\0') {
  518.         WCErrorCode = 0;
  519.         wwarning("Bad argument - service is not specified");
  520.         return NULL;
  521.     }
  522.  
  523.     if (host==NULL || host[0]=='\0')
  524.         host = "localhost";
  525.  
  526.     if ((socketaddr = getSocketAddress(host, service, protocol)) == NULL) {
  527.         WCErrorCode = 0;
  528.         wwarning("Bad address-service-protocol combination");
  529.         return NULL;
  530.     }
  531.  
  532.     /* Create the actual socket */
  533.     sock = socket(PF_INET, SOCK_STREAM, 0);
  534.     if (sock<0) {
  535.         WCErrorCode = errno;
  536.         wsyserror("Unable to create socket");
  537.         return NULL;
  538.     }
  539.     isNonBlocking = setSocketNonBlocking(sock, True);
  540.     if (connect(sock, (struct sockaddr*)socketaddr, sizeof(*socketaddr)) < 0) {
  541.         if (errno!=EINPROGRESS) {
  542.             WCErrorCode = errno;
  543.             wsyserror("Unable to make connection to address '%s:%hu'",
  544.                       inet_ntoa(socketaddr->sin_addr),
  545.                       ntohs(socketaddr->sin_port));
  546.             close(sock);
  547.             return NULL;
  548.         }
  549.     }
  550.  
  551.     cPtr = createConnectionWithSocket(sock, True);
  552.     cPtr->state = WCInProgress;
  553.     cPtr->isNonBlocking = isNonBlocking;
  554.  
  555.     /*tPtr = &cPtr->sendTimeout;
  556.      tPtr->handler = WMAddTimerHandler(tPtr->timeout*1000, connectTimeout, cPtr);
  557.     */
  558.     cPtr->handler.write = WMAddInputHandler(cPtr->sock, WIWriteMask,
  559.                                             inputHandler, cPtr);
  560.  
  561.     setConnectionAddress(cPtr, socketaddr);
  562.  
  563.     return cPtr;
  564. }
  565.  
  566.  
  567. static void
  568. removeAllHandlers(WMConnection *cPtr) /*FOLD00*/
  569. {
  570.     if (cPtr->handler.read)
  571.         WMDeleteInputHandler(cPtr->handler.read);
  572.     if (cPtr->handler.write)
  573.         WMDeleteInputHandler(cPtr->handler.write);
  574.     if (cPtr->handler.exception)
  575.         WMDeleteInputHandler(cPtr->handler.exception);
  576.     if (cPtr->sendTimeout.handler)
  577.         WMDeleteTimerHandler(cPtr->sendTimeout.handler);
  578.  
  579.     cPtr->handler.read = NULL;
  580.     cPtr->handler.write = NULL;
  581.     cPtr->handler.exception = NULL;
  582.     cPtr->sendTimeout.handler = NULL;
  583. }
  584.  
  585.  
  586. void
  587. WMDestroyConnection(WMConnection *cPtr) /*FOLD00*/
  588. {
  589.     if (cPtr->closeOnRelease && cPtr->sock>=0) {
  590.         shutdown(cPtr->sock, SHUT_RDWR);
  591.         close(cPtr->sock);
  592.     }
  593.  
  594.     removeAllHandlers(cPtr);
  595.     clearOutputQueue(cPtr);
  596.     WMFreeBag(cPtr->outputQueue);
  597.  
  598.     if (cPtr->address) {
  599.         wfree(cPtr->address);
  600.         wfree(cPtr->service);
  601.         wfree(cPtr->protocol);
  602.     }
  603.  
  604.     wrelease(cPtr);
  605. }
  606.  
  607.  
  608. void
  609. WMCloseConnection(WMConnection *cPtr) /*FOLD00*/
  610. {
  611.     if (cPtr->sock>=0) {
  612.         shutdown(cPtr->sock, SHUT_RDWR);
  613.         close(cPtr->sock);
  614.         cPtr->sock = -1;
  615.     }
  616.  
  617.     removeAllHandlers(cPtr);
  618.     clearOutputQueue(cPtr);
  619.  
  620.     cPtr->state = WCClosed;
  621. }
  622.  
  623.  
  624. WMConnection*
  625. WMAcceptConnection(WMConnection *listener) /*FOLD00*/
  626. {
  627.     struct sockaddr_in clientname;
  628.     int size;
  629.     int newSock;
  630.     WMConnection *newConnection;
  631.  
  632.     if (listener->state!=WCListening) {
  633.         wwarning("Called 'WMAcceptConnection()' on a non-listening connection");
  634.         WCErrorCode = 0;
  635.         return NULL;
  636.     }
  637.  
  638.     size = sizeof(clientname);
  639.     newSock = accept(listener->sock, (struct sockaddr*) &clientname, &size);
  640.     if (newSock<0) {
  641.         if (errno!=EAGAIN && errno!=EWOULDBLOCK) {
  642.             WCErrorCode = errno;
  643.             wsyserror("Could not accept connection");
  644.         } else {
  645.             WCErrorCode = 0;
  646.         }
  647.         return NULL;
  648.     }
  649.  
  650.     newConnection = createConnectionWithSocket(newSock, True);
  651.     WMSetConnectionNonBlocking(newConnection, True);
  652.     newConnection->state = WCConnected;
  653.     setConnectionAddress(newConnection, &clientname);
  654.  
  655.     return newConnection;
  656. }
  657.  
  658.  
  659. char*
  660. WMGetConnectionAddress(WMConnection *cPtr) /*FOLD00*/
  661. {
  662.     return cPtr->address;
  663. }
  664.  
  665.  
  666. char*
  667. WMGetConnectionService(WMConnection *cPtr) /*FOLD00*/
  668. {
  669.     return cPtr->service;
  670. }
  671.  
  672.  
  673. char*
  674. WMGetConnectionProtocol(WMConnection *cPtr)
  675. {
  676.     return cPtr->protocol;
  677. }
  678.  
  679.  
  680. int
  681. WMGetConnectionSocket(WMConnection *cPtr) /*FOLD00*/
  682. {
  683.     return cPtr->sock;
  684. }
  685.  
  686.  
  687. WMConnectionState
  688. WMGetConnectionState(WMConnection *cPtr) /*FOLD00*/
  689. {
  690.     return cPtr->state;
  691. }
  692.  
  693.  
  694. Bool
  695. WMEnqueueConnectionData(WMConnection *cPtr, WMData *data) /*FOLD00*/
  696. {
  697.     wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, False);
  698.     wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, False);
  699.  
  700.     if (cPtr->state!=WCConnected)
  701.         return False;
  702.  
  703.     WMPutInBag(cPtr->outputQueue, WMRetainData(data));
  704.     return True;
  705. }
  706.  
  707.  
  708. int
  709. WMSendConnectionData(WMConnection *cPtr, WMData *data) /*FOLD00*/
  710. {
  711.     int bytes, pos, len, totalTransfer;
  712.     TimeoutData *tPtr = &cPtr->sendTimeout;
  713.     const unsigned char *dataBytes;
  714.  
  715.     wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, -1);
  716.     wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, -1);
  717.  
  718.     if (cPtr->state!=WCConnected)
  719.         return -1;
  720.  
  721.     /* If we have no data just flush the queue, else try to send data */
  722.     if (data && WMGetDataLength(data)>0) {
  723.         WMPutInBag(cPtr->outputQueue, WMRetainData(data));
  724.         /* If there already was something in queue, and also a write input
  725.          * handler is established, it means we were unable to send, so
  726.          * return and let the write handler notify us when we can send.
  727.          */
  728.         if (WMGetBagItemCount(cPtr->outputQueue)>1 && cPtr->handler.write)
  729.             return 0;
  730.     }
  731.  
  732.     totalTransfer = 0;
  733.  
  734.     while (WMGetBagItemCount(cPtr->outputQueue) > 0) {
  735.         data = WMGetFromBag(cPtr->outputQueue, 0);
  736.         dataBytes = (const unsigned char *)WMDataBytes(data);
  737.         len = WMGetDataLength(data);
  738.         pos = cPtr->bufPos; /* where we're left last time */
  739.         while(pos < len) {
  740.         again:
  741.             bytes = write(cPtr->sock, dataBytes+pos, len - pos);
  742.             if(bytes<0) {
  743.                 switch (errno) {
  744.                 case EINTR:
  745.                     goto again;
  746.                 case EWOULDBLOCK:
  747.                     /* save the position where we're left and add a timeout */
  748.                     cPtr->bufPos = pos;
  749.                     if (!tPtr->handler) {
  750.                         tPtr->handler = WMAddTimerHandler(tPtr->timeout*1000,
  751.                                                           sendTimeout, cPtr);
  752.                     }
  753.                     if (!cPtr->handler.write) {
  754.                         cPtr->handler.write =
  755.                             WMAddInputHandler(cPtr->sock, WIWriteMask,
  756.                                               inputHandler, cPtr);
  757.                     }
  758.                     return totalTransfer;
  759.                 default:
  760.                     WCErrorCode = errno;
  761.                     cPtr->state = WCDied;
  762.                     /*clearOutputQueue(cPtr);*/
  763.                     removeAllHandlers(cPtr);
  764.                     if (cPtr->delegate && cPtr->delegate->didDie)
  765.                         (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
  766.                     return -1;
  767.                 }
  768.             }
  769.             pos += bytes;
  770.             totalTransfer += bytes;
  771.         }
  772.         WMReleaseData(data);
  773.         WMDeleteFromBag(cPtr->outputQueue, 0);
  774.         cPtr->bufPos = 0;
  775.         if (tPtr->handler) {
  776.             WMDeleteTimerHandler(tPtr->handler);
  777.             tPtr->handler = NULL;
  778.         }
  779.         if (cPtr->handler.write) {
  780.             WMDeleteInputHandler(cPtr->handler.write);
  781.             cPtr->handler.write = NULL;
  782.         }
  783.     }
  784.  
  785.     return totalTransfer;
  786. }
  787.  
  788.  
  789. /*
  790.  * WMGetConnectionAvailableData(connection):
  791.  *
  792.  * will return a WMData structure containing the available data on the
  793.  * specified connection. If connection is non-blocking (default) and no data
  794.  * is available when this function is called, an empty WMData is returned.
  795.  *
  796.  * If an error occurs while reading or the other side closed connection,
  797.  * it will return NULL.
  798.  * Also trying to read from an already died or closed connection is
  799.  * considered to be an error condition, and will return NULL.
  800.  */
  801. WMData*
  802. WMGetConnectionAvailableData(WMConnection *cPtr) /*FOLD00*/
  803. {
  804.     char buffer[NETBUF_SIZE];
  805.     int nbytes;
  806.     WMData *aData;
  807.  
  808.     wassertrv(cPtr->state!=WCNotConnected && cPtr->state!=WCListening, NULL);
  809.     wassertrv(cPtr->state!=WCInProgress && cPtr->state!=WCFailed, NULL);
  810.  
  811.     if (cPtr->state!=WCConnected)
  812.         return NULL;
  813.  
  814.     aData = NULL;
  815.  
  816. again:
  817.     nbytes = read(cPtr->sock, buffer, NETBUF_SIZE);
  818.     if (nbytes<0) {
  819.         switch (errno) {
  820.         case EINTR:
  821.             goto again;
  822.         case EWOULDBLOCK:
  823.             aData = WMCreateDataWithCapacity(0);
  824.             break;
  825.         default:
  826.             WCErrorCode = errno;
  827.             cPtr->state = WCDied;
  828.             removeAllHandlers(cPtr);
  829.             if (cPtr->delegate && cPtr->delegate->didDie)
  830.                 (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
  831.             break;
  832.         }
  833.     } else if (nbytes==0) {      /* the other side has closed connection */
  834.         cPtr->state = WCClosed;
  835.         removeAllHandlers(cPtr);
  836.         if (cPtr->delegate && cPtr->delegate->didDie)
  837.             (*cPtr->delegate->didDie)(cPtr->delegate, cPtr);
  838.     } else {
  839.         aData = WMCreateDataWithBytes(buffer, nbytes);
  840.     }
  841.  
  842.     return aData;
  843. }
  844.  
  845.  
  846. void
  847. WMSetConnectionDelegate(WMConnection *cPtr, ConnectionDelegate *delegate) /*FOLD00*/
  848. {
  849.     wassertr(cPtr->sock >= 0);
  850.     /* Don't try to set the delegate multiple times */
  851.     wassertr(cPtr->delegate == NULL);
  852.  
  853.     cPtr->delegate = delegate;
  854.     if (delegate && delegate->didReceiveInput && !cPtr->handler.read)
  855.         cPtr->handler.read = WMAddInputHandler(cPtr->sock, WIReadMask,
  856.                                                inputHandler, cPtr);
  857.     if (delegate && delegate->didCatchException && !cPtr->handler.exception)
  858.         cPtr->handler.exception = WMAddInputHandler(cPtr->sock, WIExceptMask,
  859.                                                     inputHandler, cPtr);
  860. }
  861.  
  862.  
  863. #if 0
  864. Bool
  865. WMIsConnectionNonBlocking(WMConnection *cPtr) /*FOLD00*/
  866. {
  867. #if 1
  868.     int state;
  869.  
  870.     state = fcntl(cPtr->sock, F_GETFL, 0);
  871.  
  872.     if (state < 0) {
  873.         wsyserror("Failed to get socket flags with fcntl.");
  874.         /* If we can't use fcntl on socket, this probably also means we could
  875.          * not use fcntl to set non-blocking mode, and since a socket defaults
  876.          * to blocking when created, return False as the best assumption */
  877.         return False;
  878.     }
  879.  
  880.     return ((state & NONBLOCK_OPT)!=0);
  881. #else
  882.     return cPtr->isNonBlocking;
  883. #endif
  884. }
  885. #endif
  886.  
  887.  
  888. void
  889. WMSetConnectionNonBlocking(WMConnection *cPtr, Bool flag) /*FOLD00*/
  890. {
  891.     if (cPtr->sock < 0)
  892.         return;
  893.  
  894.     if (cPtr->isNonBlocking == flag)
  895.         return;
  896.  
  897.     if (setSocketNonBlocking(cPtr->sock, flag)==True)
  898.         cPtr->isNonBlocking = flag;
  899. }
  900.  
  901.  
  902. void*
  903. WMGetConnectionClientData(WMConnection *cPtr) /*FOLD00*/
  904. {
  905.     return cPtr->clientData;
  906. }
  907.  
  908.  
  909. void
  910. WMSetConnectionClientData(WMConnection *cPtr, void *data) /*FOLD00*/
  911. {
  912.     cPtr->clientData = data;
  913. }
  914.  
  915.  
  916. unsigned int
  917. WMGetConnectionFlags(WMConnection *cPtr) /*FOLD00*/
  918. {
  919.     return cPtr->uflags;
  920. }
  921.  
  922.  
  923. void
  924. WMSetConnectionFlags(WMConnection *cPtr, unsigned int flags) /*FOLD00*/
  925. {
  926.     cPtr->uflags = flags;
  927. }
  928.  
  929.  
  930. void
  931. WMSetConnectionSendTimeout(WMConnection *cPtr, unsigned int timeout) /*FOLD00*/
  932. {
  933.     if (timeout == 0)
  934.         timeout = DEF_TIMEOUT;
  935.  
  936.     cPtr->sendTimeout.timeout = timeout;
  937. }
  938.  
  939.  
  940.